package com.alibaba.markovdemo.engine;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.markovdemo.BO.*;
import com.alibaba.markovdemo.engine.AI.GACaseGenerator.CovMock;
import com.alibaba.markovdemo.engine.stages.*;
import com.alibaba.markovdemo.engine.util.Toolkit;
import com.alibaba.markovdemo.entity.*;
import com.alibaba.markovdemo.service.*;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;


@Service
public class MultiAIPlusController {

  private static final Logger logger = LoggerFactory.getLogger(MultiAIPlusController.class);

  Gson gson = new Gson();

  String runtimeGap;
  int sucessCaseNum;
  int envNum;

  //0:普通回归 1:智能回归
  String regressionType = "1";
  boolean openSmartRegress;
  int caseNum;
  Long scenarioId;
  Long appId;
  String caseType;
  List<String> caseGroup;
  String caseBranch;
  String caseIds;
  Map<String, FailCaseBucket> failCaseBuckets;
  Map<Long, String> conflictCaseId;
  boolean regAccuracy = false;

  //dump数据统计
  Map<String, RunDs> dumpDataStaticMap;
  //分层数据统计
  Map<String, Map<String, RunDs>> envRunDataStaticMap;
  //各阶段耗时
  Map<String, Map<String, Long>> envBucketCostMap;
  //回归分析报告
  Map<String, EnvBasicReport> envReportMap;

  //搜集用例集中用到的数据类型,仅供统计使用
  Map<String, String> dataTypeMap;
  //pipeline设置的数据类型,仅供统计使用
  Map<String, String> dataTypePipelineMap;
  Map<String, Integer> envRunBucketMap;
  Map<String, GotTestcaseSnaps> gotTestcaseSnapMap;
  @Autowired
  TestcaseService gotTestcaseDao;
  @Autowired
  GotReportsService gotReportsDao;
  @Autowired
  private PipelineService pipelineService;
  @Autowired
  TestcaseSnapsService gotTestcaseSnapsDao;
  @Autowired
  CaseAccuracyService caseAccuracyService;

  GotReports gotReports;
  public List<TestCaseAI> getFailCaseAIList;
  List<GotTestcaseSnaps> gotTestcaseSnapList;

  int dumpDataRecoredNum;
  int failCaseNum;
  boolean stopFlag;
  Map<String, String> envMap;
  String envName;
  int parallerNum = 20;

  private String SERIAL = "serial";
  private static final String PARAMS = "params";

  private static final String JAVA = "JAVA";
  private static final String BLINK = "blink";

  private String ALL_DS = "allDs";
  private String SINGLE_DS = "singleDs";
  private String SINGLE_RECORD = "singleRecord";

  private String DATA_PREPARE = "dataPrepare";
  private String QUERY_CHECK = "queryCheck";


  //执行分桶
  private String DUMP_ALL_BUCKET = "dumpAllBucket";
  private String DUMP_TREE_BUCKET = "dumpTreeBucket";
  private String QUERY_CHECK_BUCKET = "queryCheckBucket";
  private String RUN_FAIL_BUCKET = "runFailBucket";

  //状态文案
  private String MULTI_RUN_STAGE_NAME = "回归阶段";
  private String DUMP_STAGE_NAME = "全量数据阶段";
  private String RUN_CASE_NAME = "运行用例阶段";
  private String ANALYSE_NAME = "报告生成阶段";
  private String TROUBLE_SHOOT = "智能排查阶段";
  //运行状态
  private String RUNNING = "RUNNING";
  private String SUCCESS = "SUCCESS";
  private String FAIL = "FAIL";
  private String EXCEPTION = "EXCEPTION";
  private String WAITING = "WAITING";
  private String STOP = "STOP";
  Gson gosn = new Gson();


  /**
   * 函数功能:功能回归初始化
   * 注意:此处为了方便demo展示就直接初始化,但在实战中需要改造,否则可能在多个回归任务并行时,全局参数会有相互覆盖的情况
   *
   * @param multiAIInfo
   */
  public void initMultiParams(MultiAIInfo multiAIInfo) {
    appId = multiAIInfo.getAppId();
    scenarioId = multiAIInfo.getScenarioId();
    //此处是为mock的环境名
    envName = multiAIInfo.getTaskName();
    this.openSmartRegress = multiAIInfo.isOpenSmartRegress();
    regAccuracy = multiAIInfo.getRegAccuracy(); //需要在每个case执行完成后，获取覆盖代码行
    caseBranch = "master";
    gotTestcaseSnapMap = new HashMap<>();
    failCaseBuckets = new HashMap<>();
    envReportMap = new HashMap<>();
    conflictCaseId = new HashMap<>();
    dataTypeMap = new HashMap<>();
    dataTypePipelineMap = new HashMap<>();
    gotTestcaseSnapMap = new HashMap<>();
    getFailCaseAIList = new ArrayList<>();
    failCaseBuckets = new HashMap<>();
    gotTestcaseSnapList = new ArrayList<>();
    dumpDataStaticMap = new HashMap<>();
    envRunDataStaticMap = new HashMap<>();
    envBucketCostMap = new HashMap<>();
    envRunBucketMap = new HashMap<>();
    sucessCaseNum = 0;
    dumpDataRecoredNum = 0;
    envNum = 0;
    failCaseNum = 0;
    stopFlag = false;
    caseNum = 0;
    //环境信息
    envMap = new HashMap<>();


  }

  /**
   * 函数功能:回归测试执行
   * 1.普通回归:caseBycase,
   * 2.智能回归:用例重新编排,高效执行
   *
   * @param multiAIInfo
   */
  public Long runIntelligent(MultiAIInfo multiAIInfo) {

    //初始化
    initMultiParams(multiAIInfo);
    String exector = "markov";
    String runType = multiAIInfo.getRunType();
    String caseType = multiAIInfo.getCaseType();
    Map<String, List<String>> envInfo = multiAIInfo.getEnvInfo();
    List<String> caseGroup = multiAIInfo.getCaseGroup();
    String caseIds = multiAIInfo.getCaseIds();
    final GotReports gotReports = new GotReports();
    gotReports.setUser(exector);
    gotReports.setAppId(appId);
    gotReports.setScenarioId(scenarioId);
    gotReports.setRunType(runType);
    gotReports.setReportName(Toolkit.implode(",", new ArrayList<>(envInfo.keySet())));
    gotReports.setStatus(ResultStatus.RUNNING.name());
    if (regAccuracy) {
      gotReports.setIsVisible(1);
    }
    //测试报告入库,注:第一次save后,gotReports对象中就会拿到id
    insertReport(gotReports);

    // 启动功能回归线程,批量执行case
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          logger.info("回归测试开启..");

          batchRunInteligentTestCasesZK(appId, scenarioId, gotReports, envInfo, caseGroup, caseBranch, caseType, caseIds);

        } catch (Exception e) {
          System.out.println("batch run case failed:" + e.getMessage());
          gotReports.setStatus(ResultStatus.ERROR.name());
          gotReports.setMessage("batch run case failed:" + e.getMessage());
          saveReport(gotReports);
        }
      }
    });
    thread.start();
    return gotReports.getId();
  }

  /**
   * 获取分组用例集
   *
   * @param scenarioId
   * @param caseGroupList
   * @return
   */
  public List<TestCaseInput> getCaseListByGroup(Long scenarioId, List<String> caseGroupList, String branch) {


    List<TestCaseInput> testcaseList = new ArrayList<>();
    if (branch == null || "".equals(branch)) {
      branch = "master";
    }

    List<TestCaseInput> testcaseListDb = gotTestcaseDao.getAllCaseByScenarioId(scenarioId);
    for (TestCaseInput oneCase : testcaseListDb) {

      String caseGroupValue = oneCase.getCaseGroup();
      if (caseGroupValue == null) {
        caseGroupValue = "默认分组";
      }
      if (caseGroupList.contains(caseGroupValue)) {
        testcaseList.add(oneCase);
      }
    }
    return testcaseList;
  }


  /**
   * 函数功能:获取回归用例集
   *
   * @param envInfo
   * @return
   */
  public List<MultiRunCaseInfo> getMultiRunCaseInfoList(Map<String, List<String>> envInfo) {

    //用例集合
    List<TestCaseInput> testcaseList = new ArrayList<>();
    List<MultiRunCaseInfo> multiRunCaseInfoList = new ArrayList<>();

    //获取用例集. 0:cases,1:scenarios
    if (scenarioId != null) {
      //多场景用例
      if (caseType != null && "1".equals(caseType)) {
        Map<String, List<String>> scenidCaseGroup = new HashMap<>();
        for (String scenId : caseGroup) {
          String[] multiStr = scenId.split("__");
          String realScenId = multiStr[0];
          String caseGroup = multiStr[2];
          if (!scenidCaseGroup.containsKey(realScenId)) {
            scenidCaseGroup.put(realScenId, new ArrayList<>());
          }
          scenidCaseGroup.get(realScenId).add(caseGroup);
        }

        for (String realScenId : scenidCaseGroup.keySet()) {
          MultiRunCaseInfo multiRunCaseInfo = new MultiRunCaseInfo();
          multiRunCaseInfo.setAppId(appId);
          multiRunCaseInfo.setScenarioId(scenarioId);
          multiRunCaseInfo.setRealAppId(appId);
          multiRunCaseInfo.setRealScenarioId(Long.valueOf(realScenId));
          List<TestCaseInput> testCaseInputList = getCaseListByGroup(Long.valueOf(realScenId), scenidCaseGroup.get(realScenId), caseBranch);
          multiRunCaseInfo.setTestcaseList(testCaseInputList);
          caseNum += testCaseInputList.size();
          multiRunCaseInfoList.add(multiRunCaseInfo);
        }

      }
      //单场景下所有用例
      else if ("0".equals(caseType)) {
        MultiRunCaseInfo multiRunCaseInfo = new MultiRunCaseInfo();
        multiRunCaseInfo.setAppId(appId);
        multiRunCaseInfo.setScenarioId(scenarioId);
        multiRunCaseInfo.setRealAppId(appId);
        multiRunCaseInfo.setRealScenarioId(scenarioId);
        if (caseGroup == null || caseGroup.size() == 0) {
          if (caseIds != null) {
            //前端提交批量运行
            String[] cases = caseIds.split(",");
            for (String caseid : cases) {
              TestCaseInput caseObj = gotTestcaseDao.getTestCaseById(Long.parseLong(caseid));
              testcaseList.add(caseObj);
            }
            multiRunCaseInfo.setTestcaseList(testcaseList);
          } else {
            if (caseBranch == null) {
              caseBranch = "master";
            }
            testcaseList = gotTestcaseDao.getAllCaseByScenarioId(scenarioId);

            multiRunCaseInfo.setTestcaseList(testcaseList);
          }
        }
        //选中的特定分组用例
        else {
          testcaseList = getCaseListByGroup(scenarioId, caseGroup, caseBranch);
          multiRunCaseInfo.setTestcaseList(testcaseList);
        }
        caseNum = testcaseList.size();
        multiRunCaseInfoList.add(multiRunCaseInfo);
      }

    } else {
      gotReports.setStatus(ResultStatus.FAILURE.name());
      saveReport(gotReports);

    }

    return multiRunCaseInfoList;
  }


  /**
   * 函数功能:载入测试环境配置
   * 说明:用户可根据自身测试系统的需求去完善
   *
   * @param envInfo
   * @param pipelineObj
   */
  public void loadEnvMapInfo(Map<String, List<String>> envInfo, JSONObject pipelineObj) {

    //todo: to be filled
    System.out.print("载入回归环境完成\n");
  }


  /**
   * 函数功能:
   * 判断是走哪种回归模式.0:caseBycase,1:智能回归
   *
   * @param pipelineObj
   */
  public void getMultiRunType(JSONObject pipelineObj) {
    if (openSmartRegress) {
      regressionType = "1";
    } else {
      regressionType = "0";
    }
    try {

      List<JSONObject> arr = (List<JSONObject>) pipelineObj.get("run-stage");
      JSONObject paramsObj = (JSONObject) arr.get(1).get("params");
      if (paramsObj.containsKey("regressionType")) {
        regressionType = (String) paramsObj.get("regressionType");
      }
    } catch (Exception e) {

    }


  }


  /**
   * 函数功能:中断回归测试流程
   *
   * @param gotReports
   * @return
   */
  boolean isTaskStop(GotReports gotReports) {

    //todo:待添加..
    return false;
  }

  /**
   * 功能: 在用例中保留一条测试数据
   *
   * @param testCaseAI
   * @param groupNum
   * @param dataType
   * @param param1
   * @param param2
   * @param mode
   */
  public void addPrepareRecord(TestCaseAI testCaseAI, int groupNum, String dataType, Object param1, Object param2, String mode) {


    LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> prepareData = testCaseAI.getPrepareData();
    //添加分组数据
    if (prepareData.size() != groupNum + 1) {
      prepareData.add(new HashMap<>());
    }
    //取的该组疏忽
    HashMap<String, LinkedList<DetailDataInfo>> eachPrepareData = new HashMap<>();
    try {
      eachPrepareData = prepareData.get(prepareData.size() - 1);
    } catch (Exception e) {
      e.printStackTrace();
    }

    //添加type维度的所有ds
    if (mode.equals(ALL_DS)) {

      LinkedList<DetailDataInfo> allDsList = (LinkedList<DetailDataInfo>) param1;
      eachPrepareData.put(dataType, allDsList);
      for (DetailDataInfo detailDataInfo : allDsList) {
        dumpDataRecoredNum += detailDataInfo.getData().size();
      }
    }
    //添加ds
    else {
      //添加ds维度的数据
      if (!eachPrepareData.containsKey(dataType)) {
        eachPrepareData.put(dataType, new LinkedList<>());
      }
      LinkedList<DetailDataInfo> eachDsPrepareData = eachPrepareData.get(dataType);

      if (mode.equals(SINGLE_DS)) {

        DetailDataInfo detailDataInfo = (DetailDataInfo) param2;
        eachDsPrepareData.add(detailDataInfo);
        dumpDataRecoredNum += detailDataInfo.getData().size();

      }
      //添加ds下的每条记录维度
      else if (mode.equals(SINGLE_RECORD)) {

        boolean addFlag = false;
        String dsName = (String) param1;
        RecordInfo recordInfo = (RecordInfo) param2;
        testCaseAI.addConflictDs(dsName, recordInfo.getKey());

        //如果ds为空
        if (eachDsPrepareData.size() == 0 || eachDsPrepareData == null) {
          DetailDataInfo detailDataInfo = new DetailDataInfo();
          detailDataInfo.setDsName(dsName);
          LinkedList<RecordInfo> recordList = new LinkedList<>();
          recordList.add(recordInfo);
          detailDataInfo.setData(recordList);
          eachDsPrepareData.add(detailDataInfo);
          dumpDataRecoredNum += 1;
        }
        //如果ds已存在
        else {
          for (DetailDataInfo detailDataInfo : eachDsPrepareData) {
            //找到ds
            if (detailDataInfo.getDsName().equals(dsName)) {
              if (detailDataInfo.getData().size() == 0 || detailDataInfo.getData() == null) {
                detailDataInfo.setData(new LinkedList<>());
              }
              detailDataInfo.getData().add(recordInfo);
              dumpDataRecoredNum += 1;
              addFlag = true;
              break;
            }
          }
          //如果找不到ds,则新建
          if (addFlag == false) {
            DetailDataInfo detailDataInfo = new DetailDataInfo();
            detailDataInfo.setDsName(dsName);
            LinkedList<RecordInfo> recordList = new LinkedList<>();
            recordList.add(recordInfo);
            detailDataInfo.setData(recordList);
            eachDsPrepareData.add(detailDataInfo);
            dumpDataRecoredNum += 1;
          }
        }
      }
    }

  }


  /**
   * 函数功能:
   * 1.整合数据准备,区分为全量数据 + 串行用例数据
   * 2.识别全部用例中的冲突数据,并进行打标
   * 3.执行全量数据准备
   *
   * @param testcaseAIList
   * @param
   * @return
   */
  public LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> getDumpData(List<TestCaseAI> testcaseAIList) {
    //注:有冲突的数据,即key一致时,value不一致为冲突,需要来标注用例为冲突,即并行.
    LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> allDumpData = new LinkedList<>();
    HashMap<String, LinkedList<DetailDataInfo>> dumpData = new HashMap<>();
    //此处我们默认TAIR可走全量,k-v形式的,当然用户可以自行决定全量数据准备的策略
    dataTypePipelineMap.put(Utils.PrepareDataType.Tair.name(), "use");

    //遍历用例集
    for (TestCaseAI testCaseAI : testcaseAIList) {
      //算法是扫描每个用例的每个ds,如果在里面的就放入,如果有冲突的就不放入,有没有用例是带数据,且能并行?那我们简单点,所有带数据的,都是要串行的.因为意味着可能有数据冲突.除非是不带,如果是全量数据,且有数据的,说明是冲突.

      if (testCaseAI.getPrepareDataSnap().size() > 0) {
        //遍历每组数据源集合
        for (int groupNum = 0; groupNum < testCaseAI.getPrepareDataSnap().size(); groupNum++) {

          HashMap<String, LinkedList<DetailDataInfo>> groupData = testCaseAI.getPrepareDataSnap().get(groupNum);
          //遍历每个数据源
          for (String dataType : groupData.keySet()) {
            dataTypeMap.put(dataType, "use");

            LinkedList<DetailDataInfo> allDsList = groupData.get(dataType);

            //如果非全量分组
            if (!dataTypePipelineMap.containsKey(dataType)) {
              //如果为非全量数据类型,则用例保留数据
              addPrepareRecord(testCaseAI, groupNum, dataType, allDsList, null, ALL_DS);
              continue;
            } else {
              if (allDsList.size() > 0) {
                //遍历每个数据源的数据list
                for (DetailDataInfo oneDsList : allDsList) {
                  //遍历每条数据记录
                  for (int recordNum = 0; recordNum < oneDsList.getData().size(); recordNum++) {
                    RecordInfo recordInfo = oneDsList.getData().get(recordNum);
                    //将数据加入全量,并删去原来的record,如果全量有冲突,则不删除
                    boolean conflictFlag = addDumpData(recordInfo, dumpData, dataType, oneDsList.getDsName());
                    //如果数据有冲突,则用例保留数据,不再记录全量桶
                    if (conflictFlag == true) {
                      addPrepareRecord(testCaseAI, groupNum, dataType, oneDsList.getDsName(), recordInfo, SINGLE_RECORD);
                      conflictCaseId.put(testCaseAI.getId(), "use");
                    }
                  }

                }
              }
            }
          }
        }
      }
    }
    allDumpData.add(dumpData);
    //获取全量桶相关统计数据
    calDumpDataStatic(allDumpData);

    return allDumpData;
  }

  /**
   * 统计报告:搜集数据源名字
   *
   * @param ds
   * @return
   */
  public String getTypeDs(String ds) {

    String[] dasource = ds.split(":");
    if (dasource.length == 3) {
      String dsName = dasource[0];
      String groupName = dasource[1];
      return dsName + ":" + groupName;
    } else {
      return dasource[0];
    }

  }

  /**
   * 函数功能:对全量桶数据进行统计
   * 包含所有记录数目,冗余数据数目
   */
  public void calDumpDataStatic(LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> allDumpData) {

    for (HashMap<String, LinkedList<DetailDataInfo>> typeInfo : allDumpData) {

      for (String type : typeInfo.keySet()) {
        LinkedList<DetailDataInfo> list = typeInfo.get(type);
        for (DetailDataInfo detailDataInfo : list) {
          String ds = detailDataInfo.getDsName();
          String typeDs = type + ":" + getTypeDs(ds);
          if (!dumpDataStaticMap.containsKey(typeDs)) {
            dumpDataStaticMap.put(typeDs, new RunDs());
          }
          dumpDataStaticMap.get(typeDs).setAllNum(dumpDataStaticMap.get(typeDs).getAllNum() + detailDataInfo.getData().size());
          dumpDataStaticMap.get(typeDs).setRepeatNum(dumpDataStaticMap.get(typeDs).getRepeatNum() + detailDataInfo.getData().size());
        }
      }
    }
  }

  /**
   * 函数功能:将一条测试数据加入全量数据准备桶中
   * 如果数据存在全量桶冲突,则不在加入全量桶
   *
   * @param recordInfo
   * @param dumpData
   * @param dsName
   * @return
   */
  boolean addDumpData(RecordInfo recordInfo, HashMap<String, LinkedList<DetailDataInfo>> dumpData, String dataType, String dsName) {


    boolean addFlag = false;
    boolean conflictFlag = false;

    //新建dataType
    if (!dumpData.containsKey(dataType)) {
      dumpData.put(dataType, new LinkedList<>());
    }

    LinkedList<DetailDataInfo> detailDataInfoList = dumpData.get(dataType);

    //如果ds为空
    if (detailDataInfoList.size() == 0 || detailDataInfoList == null) {
      DetailDataInfo detailDataInfo = new DetailDataInfo();
      detailDataInfo.setDsName(dsName);
      LinkedList<RecordInfo> recordList = new LinkedList<>();
      recordList.add(recordInfo);
      detailDataInfo.setData(recordList);
      detailDataInfoList.add(detailDataInfo);
    }
    //ds不为空
    else {
      //如果ds已存在
      for (DetailDataInfo detailDataInfo : detailDataInfoList) {
        //如果找到ds
        if (detailDataInfo.getDsName().equals(dsName)) {
          if (detailDataInfo.getData().size() == 0 || detailDataInfo.getData() == null) {
            detailDataInfo.setData(new LinkedList<>());
          }
          //遍历看是否会有冲突
          for (RecordInfo eachRecord : detailDataInfo.getData()) {
            //冲突
            if (eachRecord.getKey().equals(recordInfo.getKey()) && !eachRecord.getValue().equals(recordInfo.getValue())) {
              conflictFlag = true;
              return conflictFlag;
            }
          }
          //没冲突合并
          detailDataInfo.getData().add(recordInfo);
          addFlag = true;
          break;
        }
      }
      //如果找不到ds,则新建
      if (addFlag == false) {
        DetailDataInfo detailDataInfo = new DetailDataInfo();
        detailDataInfo.setDsName(dsName);
        LinkedList<RecordInfo> recordList = new LinkedList<>();
        recordList.add(recordInfo);
        detailDataInfo.setData(recordList);
        detailDataInfoList.add(detailDataInfo);
      }
    }

    return conflictFlag;
  }

  /**
   * 函数功能:批量执行测试用例集主流程
   *
   * @param appId
   * @param scenarioId
   * @param gotReports
   * @param envInfo
   * @param caseGroup
   * @param caseBranch
   * @param caseType
   * @param caseIds
   * @return
   * @throws Exception
   */
  public boolean batchRunInteligentTestCasesZK(final Long appId, final Long scenarioId, GotReports gotReports, Map<String, List<String>> envInfo, List<String> caseGroup, String caseBranch, String caseType, String caseIds) throws Exception {

    List<TestCaseAI> testcaseAllList = new ArrayList<>();
    try {
      //中断点
      if (isTaskStop(gotReports)) {
        return true;
      }
      //初始化
      this.gotReports = gotReports;
      this.scenarioId = scenarioId;
      this.appId = appId;
      this.caseGroup = caseGroup;
      this.caseBranch = caseBranch;
      this.caseType = caseType;
      this.caseIds = caseIds;
      Date start = new Date();
      gotReports.setStatus(ResultStatus.RUNNING.name());
      gotReports.setReportName(Toolkit.implode(",", new ArrayList<>(envInfo.keySet())));
      saveReport(gotReports);
      //环境信息
      envNum = envInfo.size();
      //todo:此处mock测试环境
      envNum = 1;
      JSONObject pipelineObj = null;
      //获取回归用例集合
      List<MultiRunCaseInfo> multiRunCaseInfoList = getMultiRunCaseInfoList(envInfo);
      gotReports.setCaseNum(caseNum);
      saveReport(gotReports);

      if (caseNum == 0) {
        System.out.print("回归用例为0,回归停止");
        gotReports.setStatus(ResultStatus.SUCCESS.name());
        gotReports.setMessage("回归用例集数目为零,回归完成");
        saveReport(gotReports);
        return true;
      }

      //多场景回归阶段:每个场景进行依次回归
      for (MultiRunCaseInfo multiRunCaseInfo : multiRunCaseInfoList) {

        //中断点
        if (isTaskStop(gotReports)) {
          return true;
        }

        System.out.print("批量执行场景用例,场景id:" + multiRunCaseInfo.getRealScenarioId() + ",用例数:" + multiRunCaseInfo.getTestcaseList().size());

        //获取单场景用例集合
        List<TestCaseInput> testcaseList = multiRunCaseInfo.getTestcaseList();
        if (testcaseList.size() == 0) {
          continue;
        }
        //pipeline快照入库
        GotPipeline gotPipeline = pipelineService.getPipeline(multiRunCaseInfo.getRealScenarioId());
        pipelineObj = JSONObject.parseObject(gotPipeline.getPipeline());
        multiRunCaseInfo.setPipelineObj(pipelineObj);

        //载入环境配置
        loadEnvMapInfo(envInfo, pipelineObj);
        //fork出一份真正执行的用例列表,该用例集带有智能化的扩展信息,在智能回归模式可用到,但在普通回归使用较少.
        List<TestCaseAI> testcaseAIList = forkTestcaseAIList(testcaseList);
        testcaseAllList.addAll(testcaseAIList);
        //确定回归模式. regressionType=1:智能回归/ regressionType=1:普通回归
        getMultiRunType(pipelineObj);

        //回归模式:智能回归
        if (!"0".equals(regressionType)) {

          System.out.print("全量数据抽取中..\n");
          //全量数据抽取,可一次性执行完成的测试数据才可放入全量数据准备桶中
          LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpData = getDumpData(testcaseAIList);
          Map<String, List<TestCaseAI>> testcaseGroup = new HashMap<>();
          testcaseGroup.put(SERIAL, testcaseAIList);

          //多个测试环境时,并行执行
          int i = 0;
          MultiEnvTreeCaseThread multiEnvTreeCaseThread;
          CountDownLatch countDownLatch = new CountDownLatch(envNum);
          List<MultiEnvTreeCaseThread> threadsList = new ArrayList<>();
          //初始化每套测试环境统计数据
          if (!envRunBucketMap.containsKey(envName)) {
            envRunBucketMap.put(envName, 0);
          }
          if (!envRunDataStaticMap.containsKey(envName)) {
            envRunDataStaticMap.put(envName, new HashMap<>());
          }
          if (!envBucketCostMap.containsKey(envName)) {
            envBucketCostMap.put(envName, new HashMap<>());
          }

          //将测试数据集均匀分配给多个测试环境
          Map<String, List<TestCaseAI>> testcaseEnvGroup = getTestcaseEnvGroup(testcaseGroup, envNum, i);
          System.out.print("启动用例回归线程" + i);
          multiEnvTreeCaseThread = new MultiEnvTreeCaseThread(testcaseEnvGroup.get(SERIAL), pipelineObj, parallerNum, gotReports, countDownLatch, envName, dumpData, multiRunCaseInfo.getRealScenarioId());
          multiEnvTreeCaseThread.setDaemon(true);
          multiEnvTreeCaseThread.start();
          threadsList.add(multiEnvTreeCaseThread);
          Thread.currentThread().sleep(3000);
          i++;

          //主进程阻塞,如果部署线程执行结束则往下
          countDownLatch.await();
          System.out.print("=========执行完成,开始记录失败用例========\n");

          //搜集所有测试环境的失败用例,放入失败重试桶中
          for (MultiEnvTreeCaseThread thread : threadsList) {
            gotTestcaseSnapMap.putAll(thread.getTestcaseSnapMap());
            failCaseBuckets.putAll(thread.getFailCaseBuckets());
          }
        }
        //回归模式:caseBycase的普通回归
        else {
          Map<String, List<TestCaseAI>> testcaseGroup = new HashMap<>();
          for (TestCaseAI testCaseAI : testcaseAIList) {
            testCaseAI.setPrepareData(testCaseAI.getPrepareDataSnap());
          }
          //全部用例都设置为串行分组
          testcaseGroup.put(SERIAL, testcaseAIList);
          int i = 0;
          //多环境则启动多个线程并行执行
          MultiEnvSimpleCaseThread multiEnvSimpleCaseThread;
          CountDownLatch countDownLatch = new CountDownLatch(envNum);
          List<MultiEnvSimpleCaseThread> threadsList = new ArrayList<>();
          //todo:此处demo暂时mock一个测试环境
          Map<String, String> envMap = new HashMap<>();
          envMap.put("env1", "envName1");
          envNum = envMap.size();
          //多环境并发
          for (String envName : envMap.keySet()) {
            if (!envRunBucketMap.containsKey(envName)) {
              envRunBucketMap.put(envName, 0);
            }
            //将用例集分发到多个环境上
            Map<String, List<TestCaseAI>> testcaseEnvGroup = getTestcaseEnvGroup(testcaseGroup, envNum, i);
            System.out.print("启动用例回归线程" + i);
            multiEnvSimpleCaseThread = new MultiEnvSimpleCaseThread(testcaseEnvGroup.get(SERIAL), pipelineObj, gotReports, countDownLatch, envName);
            multiEnvSimpleCaseThread.setDaemon(true);
            multiEnvSimpleCaseThread.start();
            threadsList.add(multiEnvSimpleCaseThread);
            Thread.currentThread().sleep(3000);
            i++;
          }
          //主进程阻塞
          countDownLatch.await();
        }
      }

      //中断点
      if (isTaskStop(gotReports)) {
        return true;
      }
      //失败重试桶
      reRunFailCaseBuckets(gotReports);


      //测试报告统计
      int failureCount = 0;
      for (GotTestcaseSnaps testRespone : gotTestcaseSnapMap.values()) {
        if (!testRespone.getStatus().equals(ResultStatus.SUCCESS.name())) {
          failureCount++;
        }
      }

      if (failureCount == 0) {
        gotReports.setStatus(ResultStatus.SUCCESS.name());
      } else {
        gotReports.setStatus(ResultStatus.FAILURE.name());
      }
      Date end = new Date();
      runtimeGap = String.valueOf(end.getTime() - start.getTime());

      System.out.print("开始搜集回归数据...\n");
      //测试报告详细分析
      gotReports.setAnalysis(setAnalysisReportPlus());

      saveReport(gotReports);
      System.out.print("测试报告产出完成");


    } catch (Exception e) {
      System.out.print("回归异常:" + Toolkit.getErrorStackTrace(e));
      gotReports.setStatus(ResultStatus.FAILURE.name());
      gotReports.setMessage(e.getMessage());
      saveReport(gotReports);
    }

    return true;
  }

  /**
   * 函数功能:智能回归测试报告详情分析
   *
   * @return
   */
  public String setAnalysisReportPlus() {

    //1.ds维度,总调用,实际调用,冗余度,平均耗时,节省耗时,
    //2.执行维度,总串行数,实际串行数,总并行数,节省耗时
    //3.总体信息:总用例,智能回归:打开,数据冗余度,执行冗余度,整体耗时,失败用例数,成功用例数,重跑用例数,重跑耗时,重跑成功,重跑失败,重跑通过率.

    AIReportPlusInfo aiReportInfo = new AIReportPlusInfo();
    //获取整体报告统计数据
    BasicReport allReport = statisBasicReport(gotTestcaseSnapMap);
    System.out.print("[整体报告统计数据]搜集完成!\n");
    //获取分环境报告数据
    List<EnvBasicReport> envReportList = statisEnvReportList(gotTestcaseSnapMap);
    System.out.print("[分环境报告数据]搜集完成!\n");
    //获取ds维度的图标信息
    Map<String, ChartBasicInfo> treeDsChart = statisTreeChart(envRunDataStaticMap);
    System.out.print("[分层数据统计图标]搜集完成!\n");
    //获取全量ds维度的图标信息
    Map<String, ChartBasicInfo> dumpDsChart = statisDumpAllChart(dumpDataStaticMap);
    System.out.print("[全量数据统计图标]搜集完成!\n");

    aiReportInfo.setBasicReport(allReport);
    aiReportInfo.setEnvReportList(envReportList);
    aiReportInfo.setTreeDsChart(treeDsChart);
    aiReportInfo.setDumpDsChart(dumpDsChart);

    System.out.print("统计数据:\n" + gson.toJson(aiReportInfo));

    return gson.toJson(aiReportInfo);
  }


  /**
   * 函数功能:获取分环境报告数据
   *
   * @param gotTestcaseSnapMap
   * @return
   */
  public List<EnvBasicReport> statisEnvReportList(Map<String, GotTestcaseSnaps> gotTestcaseSnapMap) {
    List<EnvBasicReport> envReportList = new ArrayList<>();
    try {
      int sucessCaseNum;
      int failCaseNum;
      int caseNum = 0;

      //计算all
      for (GotTestcaseSnaps gotTestcaseSnaps : gotTestcaseSnapMap.values()) {
        envReportMap.get(gotTestcaseSnaps.getEnvName()).setCaseNum(++caseNum);
        if (gotTestcaseSnaps.getStatus().equals(ResultStatus.SUCCESS.name())) {
          sucessCaseNum = envReportMap.get(gotTestcaseSnaps.getEnvName()).getSucessCaseNum();
          envReportMap.get(gotTestcaseSnaps.getEnvName()).setSucessCaseNum(++sucessCaseNum);
        } else {
          failCaseNum = envReportMap.get(gotTestcaseSnaps.getEnvName()).getFailCaseNum();
          envReportMap.get(gotTestcaseSnaps.getEnvName()).setFailCaseNum(++failCaseNum);
        }
      }
      for (String envName : envReportMap.keySet()) {
        EnvBasicReport envReport = envReportMap.get(envName);
        envReport.setEnvName(envName);
        envReport = calMoreEnvStatic(envReport);
        envReport.setEnvName(envName);
        envReportList.add(envReport);
      }

    } catch (Exception e) {
      System.out.println(e.getMessage());
    }

    return envReportList;

  }

  /**
   * 补充各个环境阶段数据
   *
   * @param envReport
   * @return
   */
  public EnvBasicReport calMoreEnvStatic(EnvBasicReport envReport) {
    String envName = envReport.getEnvName();

    //智能回归统计
    if ("1".equals(regressionType)) {
      //补充dump全量阶段
      envReport = adDumpAllDataToEnvBasicReport(envReport, dumpDataStaticMap, envBucketCostMap.get(envName));
      //补充dump分层阶段
      envReport = addTreeDataToEnvBasicReport(envReport, envRunDataStaticMap.get(envName), envBucketCostMap.get(envName));
      //补充执行信息
      envReport = addRunToEnvBasicReport(envReport, envRunBucketMap.get(envName), envBucketCostMap.get(envName));
    } else {
      envReport = addRunToEnvBasicReport(envReport, envRunBucketMap.get(envName), envBucketCostMap.get(envName));
    }

    return envReport;
  }

  /**
   * 功能:补充快速执行统计信息
   * /快速用例运行桶(总串行数/实际串行数/总并行数/节省时间sec/提速效率/总时间)
   *
   * @param envReport
   * @return
   */
  public EnvBasicReport addRunToEnvBasicReport(EnvBasicReport envReport, Integer runSerialActualNum, Map<String, Long> bucketCostMap) {

    //串行用例数
    Integer runSerialNum = envReport.getCaseNum();
    //实际串行用例数:runSerialActualNum
    int poolNum = runSerialNum / runSerialActualNum;

    if (poolNum > 10) {
      runSerialActualNum = runSerialNum / 10;
    }
    //并行用例数
    Integer runParallelNum = runSerialNum - runSerialActualNum;
    //总时间
    Integer cost = Integer.valueOf(String.valueOf(bucketCostMap.get(QUERY_CHECK_BUCKET)));
    //提速效率
    String speedUp = changePecentFormat(Long.valueOf(runSerialNum - runSerialActualNum), Long.valueOf(runSerialActualNum));
    //节省时间
    Integer saveCost = cost / runSerialActualNum * runParallelNum;

    envReport.setRunSerialNum(runSerialNum);
    envReport.setRunSerialActualNum(runSerialActualNum);
    envReport.setRunParallelNum(runParallelNum);
    envReport.setRunSaveCost(Toolkit.changeCostFormat(Long.valueOf(saveCost)));
    envReport.setRunSpeedUpEfficiency(speedUp);
    envReport.setRunTreeTime(Toolkit.changeCostFormat(Long.valueOf(cost)));

    return envReport;
  }


  /**
   * 函数功能:分数格式转换
   *
   * @param a
   * @param b
   * @return
   */
  public String changePecentFormat(Long a, Long b) {


    if (b == 0 || a == 0) {
      return "0";
    }

    float bb = (float) a / (float) b;
    DecimalFormat decimalFormat = new DecimalFormat("0");//构造方法的字符格式这里如果小数不足2位,会以0补足.
    return decimalFormat.format(bb * 100) + "%";
  }


  /**
   * 函数功能:补充分层数据统计信息
   * 分层数据准备桶(总条数/实际执行条数/冗余度/节省时间sec/提速效率/总时间)
   *
   * @param envReport
   * @return
   */
  public EnvBasicReport addTreeDataToEnvBasicReport(EnvBasicReport envReport, Map<String, RunDs> runDataStaticMap, Map<String, Long> bucketCostMap) {

    //用例依赖总数据量
    Integer allNum = 0;
    //重复次数
    Integer repeatNum = 0;
    //执行数据条数
    Integer actrualNum = 0;
    //总耗时
    Integer cost = Integer.valueOf(String.valueOf(bucketCostMap.get(DUMP_TREE_BUCKET)));
    //预估普通回归耗时
    Integer predictCost = 0;


    for (String ds : runDataStaticMap.keySet()) {
      RunDs runDsInfo = runDataStaticMap.get(ds);
      actrualNum += runDsInfo.getRecordNum();
      allNum += runDsInfo.getAllNum();
      predictCost += runDsInfo.getCost();
    }
    //重复条
    repeatNum = allNum - actrualNum;
    Float redundenPecent = (float) repeatNum / allNum;
    //冗余度
    String redundency = changePecentFormat(Long.valueOf(repeatNum), Long.valueOf(allNum));//format 返回的是字符串
    //节省时间
    Integer saveCost = (int) (cost * redundenPecent / (1 - redundenPecent));
    //提速效率计算
    String speedUp = changePecentFormat(Long.valueOf(saveCost), Long.valueOf(cost));
    envReport.setDumpTreeRecordNum(allNum);
    envReport.setDumpTreeActualRecordNum(actrualNum);
    envReport.setDumpTreeSaveCost(Toolkit.changeCostFormat(Long.valueOf(saveCost)));
    envReport.setDumpTreeSpeedUpEfficiency(speedUp);
    envReport.setDumpTreeTime(Toolkit.changeCostFormat(Long.valueOf(cost)));
    envReport.setRedundency(redundency);

    return envReport;
  }


  /**
   * 函数功能:补充全量数据
   *
   * @param envReport
   * @param runDataStaticMap
   * @param bucketCostMap
   * @return
   */
  public EnvBasicReport adDumpAllDataToEnvBasicReport(EnvBasicReport envReport, Map<String, RunDs> runDataStaticMap, Map<String, Long> bucketCostMap) {

    //用例依赖总数据量
    Integer allNum = 0;
    //执行数据条数
    Integer actrualNum = 0;
    //总耗时
    Integer cost = Integer.valueOf(String.valueOf(bucketCostMap.get(DUMP_ALL_BUCKET)));


    for (String ds : runDataStaticMap.keySet()) {
      RunDs runDsInfo = runDataStaticMap.get(ds);
      actrualNum += runDsInfo.getRecordNum();
      allNum += runDsInfo.getAllNum();
    }

    envReport.setDumpAllrecordNum(actrualNum);
    envReport.setDumpAllTime(Toolkit.changeCostFormat(Long.valueOf(cost)));

    return envReport;
  }

  /**
   * 函数功能:获取分环境报告数据
   *
   * @param
   * @return
   */
  public Map<String, ChartBasicInfo> statisTreeChart(Map<String, Map<String, RunDs>> envRunDataStaticMap) {


    Map<String, ChartBasicInfo> chartBasicInfo = new HashMap<>();
    for (String envName : envRunDataStaticMap.keySet()) {
      for (String ds : envRunDataStaticMap.get(envName).keySet()) {
        if (!chartBasicInfo.containsKey(ds)) {
          chartBasicInfo.put(ds, new ChartBasicInfo());
        }
        RunDs runDsInfo = envRunDataStaticMap.get(envName).get(ds);
        //智能回归
        if (openSmartRegress) {
          chartBasicInfo.get(ds).setAllNum(chartBasicInfo.get(ds).getAllNum() + runDsInfo.getAllNum());
        } else {
          chartBasicInfo.get(ds).setAllNum(chartBasicInfo.get(ds).getActualNum() + runDsInfo.getRecordNum());
        }
        chartBasicInfo.get(ds).setActualNum(chartBasicInfo.get(ds).getActualNum() + runDsInfo.getRecordNum());

      }
    }
    return chartBasicInfo;

  }

  /**
   * 函数功能:获取分环境报告数据
   *
   * @param
   * @return
   */
  public Map<String, ChartBasicInfo> statisDumpAllChart(Map<String, RunDs> dumpDataStaticMap) {

    Map<String, ChartBasicInfo> chartBasicInfo = new HashMap<>();
    for (String ds : dumpDataStaticMap.keySet()) {
      if (!chartBasicInfo.containsKey(ds)) {
        chartBasicInfo.put(ds, new ChartBasicInfo());
      }
      RunDs runDsInfo = dumpDataStaticMap.get(ds);
      chartBasicInfo.get(ds).setAllNum(chartBasicInfo.get(ds).getAllNum() + runDsInfo.getAllNum());
      chartBasicInfo.get(ds).setActualNum(chartBasicInfo.get(ds).getActualNum() + runDsInfo.getRecordNum());

    }

    return chartBasicInfo;

  }

  /**
   * 函数功能:获取整体报告统计数据
   *
   * @param gotTestcaseSnapMap
   * @return
   */
  public BasicReport statisBasicReport(Map<String, GotTestcaseSnaps> gotTestcaseSnapMap) {

    BasicReport allReport = new BasicReport();
    int caseNum = 0;
    //计算all
    for (GotTestcaseSnaps gotTestcaseSnaps : gotTestcaseSnapMap.values()) {
      //计算总用例数
      caseNum++;
      //计算成功失败用例总数
      if (gotTestcaseSnaps.getStatus().equals(ResultStatus.SUCCESS.name())) {
        sucessCaseNum++;
      } else {
        failCaseNum++;
      }
    }
    allReport.setCaseNum(caseNum);
    allReport.setFailCaseNum(failCaseNum);
    allReport.setSucessCaseNum(sucessCaseNum);
    allReport.setEnvNum(envNum);
    allReport.setRunTime(Toolkit.changeCostFormat(Long.valueOf(runtimeGap)));
    if (openSmartRegress) {
      allReport.setRegressionType("智能回归");
    } else {
      allReport.setRegressionType("普通回归");
    }

    return allReport;
  }


  /**
   * 类功能:智能回归
   * 主要:对用例进行树分组,多个用例聚合并行执行,提升速率
   */
  class MultiEnvTreeCaseThread extends Thread {

    public JSONObject pipelineObj;
    public int parallelNum;
    public GotReports gotReports;
    public List<TestRespone> testResponeList;
    public CountDownLatch countDownLatch;
    public String envName;
    public List<TestCaseAI> testcaseList;
    public LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpData;
    public Queue<QueueNode> runQueue;
    public Map<String, String> resetDs;
    public Map<String, JSONObject> resetModuleInfo;
    public Map<String, GotTestcaseSnaps> gotTestcaseSnapMap;
    public List<TestCaseAI> getFailCaseAIList;
    public Map<String, FailCaseBucket> getFailCaseBuckets;
    public FailCaseBucket failCaseBucket;
    public Long scenarioId;


    public MultiEnvTreeCaseThread(List<TestCaseAI> testcaseList, JSONObject pipelineObj, int parallelNum, GotReports gotReports, CountDownLatch countDownLatch, String envName, LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpData, Long scenarioId) {
      this.parallelNum = parallelNum;
      this.gotReports = gotReports;
      this.pipelineObj = pipelineObj;
      this.countDownLatch = countDownLatch;
      this.envName = envName;
      this.testcaseList = testcaseList;
      this.runQueue = new LinkedList<>();
      this.dumpData = dumpData;
      this.resetDs = new HashMap<>();
      this.resetModuleInfo = new HashMap<>();
      this.gotTestcaseSnapMap = new HashMap<>();
      this.getFailCaseAIList = new ArrayList<>();
      this.getFailCaseBuckets = new HashMap<>();
      this.gotReports = gotReports;
      this.pipelineObj = pipelineObj;
      this.scenarioId = scenarioId;
      this.failCaseBucket = new FailCaseBucket();

    }

    public Map<String, FailCaseBucket> getFailCaseBuckets() {

      failCaseBucket.setPipelineObj(pipelineObj);
      failCaseBucket.setGotReports(gotReports);
      failCaseBucket.setTestCaseAIList(getFailCaseAIList);
      getFailCaseBuckets.put(envName, failCaseBucket);

      return getFailCaseBuckets;
    }


    public Map<String, GotTestcaseSnaps> getTestcaseSnapMap() {
      return gotTestcaseSnapMap;
    }


    /**
     * 函数功能:run入口
     * 用例树的建立 => 全量数据准备 => 分层数据准备 => 快速并行执行
     */
    @Override
    public void run() {

      Date begin = new Date();

      try {

        testResponeList = new ArrayList<>();

        if (testcaseList.size() > 0) {
          if (isTaskStop(gotReports)) {
            countDownLatch.countDown();
            return;
          }

          //STEP1:用例执行树的建立
          buildQueue(testcaseList, "");
          //STEP2:执行队列重排,将需要重启链路放置最后
          runQueue = reSortQueue();

          //STEP3:全量数据准备,去随机,主要是key-value这类明确的
          Date start = new Date();
          //STEP4:全量数据准备
          dataPrepareRun(dumpData, envName, pipelineObj, DUMP_ALL_BUCKET);
          Date end = new Date();
          envBucketCostMap.get(envName).put(DUMP_ALL_BUCKET, end.getTime() - start.getTime());
          if (isTaskStop(gotReports)) {
            countDownLatch.countDown();
            return;
          }
          //STEP5:分层数据准备 && STEP4:用例并行执行
          multiLayerPrepareAndRun();

        }
      } catch (Exception e) {
        System.out.print("用例编排报错:原因:" + Toolkit.getErrorStackTrace(e));
      }

      Date end = new Date();
      if (!envReportMap.containsKey(envName)) {
        envReportMap.put(envName, new EnvBasicReport());
      }

      envReportMap.get(envName).setRunTime(Toolkit.changeCostFormat(end.getTime() - begin.getTime()));
      countDownLatch.countDown();
    }

    /**
     * 函数功能:将传入的数据集进行一次准备
     *
     * @return
     * @throws Exception
     */
    public JSONObject dataPrepareRun(LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpData, String envName, JSONObject pipelineObj, String type) {

      //每个阶段需要透出的全局数据
      Map<StageName, Object> stageParams = new HashMap<>();

      try {
        Map<String, RunDs> runDsMap;
        //全量数据桶
        if (type.equals(DUMP_ALL_BUCKET)) {
          runDsMap = dumpDataStaticMap;
        }
        //分层数据桶
        else {
          runDsMap = envRunDataStaticMap.get(envName);
        }
        //占位符替换
        String dumpDataStr = placeholderReplace(dumpData);
        System.out.print("数据准备执行:占位符替换完成!");

        //加入部署数据
        stageParams.put(StageName.deploy, envName);

        PipelineImpl pipelineExec = new PipelineImpl();

        pipelineExec.build(pipelineObj);

        //获取prepareData阶段执行插件
        BaseStage bs = pipelineExec.flow.get(StageName.prepareData);
        //设置数据准备输入
        bs.setIputData(JSONArray.parse(dumpDataStr));
        bs.setScenarioId(scenarioId);
        //初始化RunData
        bs.setRunData();
        //设置stage之间传递的参数
        bs.setStageParams(stageParams);

        bs.beforeExec();
        bs.exec();
        bs.afterExec();

        System.out.print("数据准备执行:数据准备完成!\n");
        //填充返回结果
        List<RunData> runDataList = bs.getRunDataList();
        //统计每条记录的消耗时间
        for (RunData runData : runDataList) {

          if (runData.getRunDsMap().size() == 0) {
            continue;
          }

          for (String dsStr : runData.getRunDsMap().keySet()) {
            if (!runDsMap.containsKey(dsStr)) {
              runDsMap.put(dsStr, new RunDs());
            }
            runDsMap.get(dsStr).setRecordNum(runDsMap.get(dsStr).getRecordNum() + runData.getRunDsMap().get(dsStr).getRecordNum());
            runDsMap.get(dsStr).setCost(runDsMap.get(dsStr).getCost() + runData.getRunDsMap().get(dsStr).getCost());
          }

        }

      }
      //如有抛异常
      catch (Exception e) {
        logger.error("全量数据准备过程出现异常,中断全量准备,异常原因:\n" + Toolkit.getErrorStackTrace(e));
      }
      JSONObject res = new JSONObject();
      res.put("stageParams", stageParams);
      return res;
    }


    /**
     * 功能:整合为数据准备插件可识别的形式
     *
     * @param recordInfo
     * @param dataType
     * @param dsName
     * @return
     */
    public LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> getPrepareDataFormat(RecordInfo recordInfo, String dataType, String dsName) {

      HashMap<String, LinkedList<DetailDataInfo>> detailDataMap = new HashMap<>();
      LinkedList<DetailDataInfo> detailDataList = new LinkedList<>();
      LinkedList<RecordInfo> recordInfoList = new LinkedList<>();
      recordInfoList.add(recordInfo);
      DetailDataInfo detailDataInfo = new DetailDataInfo();
      detailDataInfo.setDsName(dsName);
      detailDataInfo.setData(recordInfoList);
      detailDataList.add(detailDataInfo);
      detailDataMap.put(dataType, detailDataList);
      LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> groupRunData = new LinkedList<>();
      groupRunData.add(detailDataMap);
      return groupRunData;
    }


    /**
     * 功能:处理为data和调用频率的矩阵
     *
     * @param testcaseList
     * @return
     */
    public HashMap<String, DataMatrixUnit> calDataMatrix(List<TestCaseAI> testcaseList) {


      HashMap<String, DataMatrixUnit> dataMatrix = new HashMap<>();
      final Base64.Encoder encoder = Base64.getEncoder();
      DataMatrixUnit dataMatrixUnit;

      for (TestCaseAI testCaseAI : testcaseList) {

        //遍历每组
        for (HashMap<String, LinkedList<DetailDataInfo>> group : testCaseAI.getPrepareData()) {

          //遍历每个type
          for (String dataType : group.keySet()) {
            LinkedHashMap<String, Object> dataKeyMap = new LinkedHashMap<>();

            //遍历每个ds
            for (DetailDataInfo detailDataInfo : group.get(dataType)) {

              Boolean reStartFlag = false;
              String dsName = detailDataInfo.getDsName();
              String ds = getTypeDs(dsName);


              //遍历每个具体数据data
              for (RecordInfo recordInfo : detailDataInfo.getData()) {
                String key = recordInfo.getKey();
                String value = recordInfo.getValue();
                dataKeyMap.put("key", key);
                dataKeyMap.put("value", value);
                dataKeyMap.put("ds", ds);
                String dataKey = new String(encoder.encode(gson.toJson(dataKeyMap).getBytes()));

                //如果第一个
                if (!dataMatrix.containsKey(dataKey)) {
                  dataMatrixUnit = new DataMatrixUnit();
                  dataMatrixUnit.addCaseid(testCaseAI.getId(), dataKey);
                } else {
                  dataMatrixUnit = dataMatrix.get(dataKey);
                  dataMatrixUnit.addCaseid(testCaseAI.getId(), dataKey);
                }
                dataMatrixUnit.setRestartFlag(reStartFlag);
                dataMatrixUnit.setName(dsName);
                dataMatrixUnit.setPrepareData(getPrepareDataFormat(recordInfo, dataType, dsName));

                dataMatrix.put(dataKey, dataMatrixUnit);
              }
            }
          }
        }
      }

      return dataMatrix;
    }


    /**
     * 对比策略是重启+max > max
     *
     * @param dataMatrixUnit1
     * @param dataMatrixUnit2
     * @return
     */
    public DataMatrixUnit compareTo(DataMatrixUnit dataMatrixUnit1, DataMatrixUnit dataMatrixUnit2) {

      int score1 = 0;
      int score2 = 0;

      if (dataMatrixUnit1 == null) {
        return dataMatrixUnit2;
      } else if (dataMatrixUnit2 == null) {
        return dataMatrixUnit1;
      }


      if (dataMatrixUnit1.isRestartFlag()) {
        score1 += 2;
      }
      if (dataMatrixUnit2.isRestartFlag()) {
        score2 += 2;
      }
      if (dataMatrixUnit1.getCallNum() >= dataMatrixUnit2.getCallNum()) {
        score1 += 1;
      } else {
        score2 += 1;
      }
      if (score1 > score2) {
        return dataMatrixUnit1;
      } else {
        return dataMatrixUnit2;
      }

    }

    /**
     * 获取最大队列Node
     *
     * @param matrix
     * @return
     */
    public QueueNode getMaxAndRebootQueueNode(HashMap<String, DataMatrixUnit> matrix) {

      QueueNode queueNode = new QueueNode();
      DataMatrixUnit dataMatrixMaxUnit = null;

      for (String dataKey : matrix.keySet()) {
        DataMatrixUnit dataMatrixUnit = matrix.get(dataKey);
        dataMatrixMaxUnit = compareTo(dataMatrixMaxUnit, dataMatrixUnit);
      }
      queueNode.setRestartFlag(dataMatrixMaxUnit.isRestartFlag());
      queueNode.setCaseids(dataMatrixMaxUnit.getCaseids());
      queueNode.setPrepareData(dataMatrixMaxUnit.getPrepareData());
      queueNode.setCaseNum(dataMatrixMaxUnit.getCaseids().size());
      queueNode.setDataKeyList(dataMatrixMaxUnit.getDataKeyList());
      queueNode.setDataType(DATA_PREPARE);
      return queueNode;
    }


    /**
     * 功能:对testCase进行剪枝,去除公共数据
     *
     * @param testCaseAI
     * @param dataKeyList
     * @return
     */
    public TestCaseAI cutCaseData(TestCaseAI testCaseAI, List<String> dataKeyList) {

      final Base64.Encoder encoder = Base64.getEncoder();

      TestCaseAI testCaseCutAI = (TestCaseAI) testCaseAI.clone();
      //遍历每组
      for (int groupNum = testCaseAI.getPrepareData().size() - 1; groupNum >= 0; groupNum--) {
        HashMap<String, LinkedList<DetailDataInfo>> group = testCaseAI.getPrepareData().get(groupNum);
        LinkedHashMap<String, Object> dataKeyMap = new LinkedHashMap<>();

        //遍历每个type
        for (String dataType : group.keySet()) {

          //遍历每个ds
          for (int detailNum = group.get(dataType).size() - 1; detailNum >= 0; detailNum--) {
            DetailDataInfo detailDataInfo = group.get(dataType).get(detailNum);
            String dsName = detailDataInfo.getDsName();
            String ds = getTypeDs(dsName);

            //遍历每个具体数据data
            for (int recordNum = detailDataInfo.getData().size() - 1; recordNum >= 0; recordNum--) {
              RecordInfo recordInfo = detailDataInfo.getData().get(recordNum);
              String key = recordInfo.getKey();
              String value = recordInfo.getValue();
              dataKeyMap.put("key", key);
              dataKeyMap.put("value", value);
              dataKeyMap.put("ds", ds);
              String dataKey = new String(encoder.encode(gson.toJson(dataKeyMap).getBytes()));
              //剪枝
              if (dataKeyList.contains(dataKey)) {
                testCaseCutAI.getPrepareData().get(groupNum).get(dataType).get(detailNum).getData().remove(recordNum);
                if (testCaseCutAI.getPrepareData().get(groupNum).get(dataType).get(detailNum).getData().size() == 0) {
                  testCaseCutAI.getPrepareData().get(groupNum).get(dataType).remove(detailNum);
                }
                if (testCaseCutAI.getPrepareData().get(groupNum).get(dataType).size() == 0) {
                  testCaseCutAI.getPrepareData().get(groupNum).remove(dataType);
                }
                if (testCaseCutAI.getPrepareData().get(groupNum).size() == 0) {
                  testCaseCutAI.getPrepareData().remove(groupNum);
                }
              }

            }
          }
        }
      }

      return testCaseCutAI;
    }


    /**
     * 功能:将testcaselist进行剪枝,并进行分组
     *
     * @param queueNode
     * @param testcaseList
     * @return
     */
    public MatrixInfo cutTestcaseList(QueueNode queueNode, List<TestCaseAI> testcaseList) {

      List<TestCaseAI> testcaseLeftList = new ArrayList<>();
      List<TestCaseAI> testcaseRightList = new ArrayList<>();
      MatrixInfo matrixInfo = new MatrixInfo();
      for (TestCaseAI testCaseAI : testcaseList) {

        if (queueNode.getCaseids().contains(testCaseAI.getId())) {
          testCaseAI = cutCaseData(testCaseAI, queueNode.getDataKeyList());
          testcaseLeftList.add(testCaseAI);
        } else {
          testcaseRightList.add(testCaseAI);
        }
      }

      matrixInfo.setLeftCaseList(testcaseLeftList);
      matrixInfo.setRightCaseList(testcaseRightList);
      return matrixInfo;
    }

    /**
     * 函数功能:更新matrix并对列表进行剪枝处理
     * 1.每次能在传递进来的队列中算出:数据<=>依赖用例数目的矩阵.
     * 2.更新抽取data后的list.并划分左右子树.
     *
     * @param testcaseList
     * @return
     */
    public MatrixInfo updataMatrixAndCutCases(List<TestCaseAI> testcaseList) {


      String leftTreeReStartFlag = "0";
      String rightTreeReStartFlag = "0";

      //数据分组优先级:重启数据源 > 最大公共数据源
      MatrixInfo matrixInfo = new MatrixInfo();
      //计算获取:数据<=>依赖用例的矩阵
      HashMap<String, DataMatrixUnit> matrix = calDataMatrix(testcaseList);
      //全叶子节点,不需要剪枝
      if (matrix.size() == 0) {
        matrixInfo.setMatrix(matrix);
        QueueNode queueNode = new QueueNode();
        queueNode.setDataType(QUERY_CHECK);
        queueNode.addCaseList(testcaseList);
        queueNode.setRestartFlag(false);
        matrixInfo.setLeftTreeReStartFlag(leftTreeReStartFlag);
        matrixInfo.setRightTreeReStartFlag(rightTreeReStartFlag);
        matrixInfo.setQueueUnit(queueNode);

        return matrixInfo;
      }
      //非叶子节点,需要剪枝,处理最大queueNode
      QueueNode queueNode = getMaxAndRebootQueueNode(matrix);
      //判断该节点是否有重启
      if (queueNode.restartFlag) {
        leftTreeReStartFlag = "1";
      }
      //剪枝
      MatrixInfo caselist = cutTestcaseList(queueNode, testcaseList);
      matrixInfo.setMatrix(matrix);
      matrixInfo.setQueueUnit(queueNode);
      matrixInfo.setLeftCaseList(caselist.getLeftCaseList());
      matrixInfo.setRightCaseList(caselist.getRightCaseList());
      matrixInfo.setLeftTreeReStartFlag(leftTreeReStartFlag);
      matrixInfo.setRightTreeReStartFlag(rightTreeReStartFlag);

      return matrixInfo;

    }


    /**
     * 函数功能:递归创建深度优先执行树
     * 树的执行顺序由队列表示
     *
     * @param testcaseList
     */
    public void buildQueue(List<TestCaseAI> testcaseList, String restartPath) {

      if (testcaseList.size() == 0) {
        return;
      }
      //获取数据频率矩阵 && 剪枝caseList
      MatrixInfo matrixInfo = updataMatrixAndCutCases(testcaseList);
      HashMap<String, DataMatrixUnit> matrix = matrixInfo.getMatrix();

      //全叶子节点
      if (matrix.size() == 0) {
        QueueNode queueUnit = matrixInfo.getQueueUnit();
        queueUnit.setRestartPath(restartPath + queueUnit.getRestartPath());
        runQueue.offer(queueUnit);
        return;
      }
      //非全叶子节点
      List<TestCaseAI> leftCastList = matrixInfo.getLeftCaseList();
      List<TestCaseAI> rightCastList = matrixInfo.getRightCaseList();
      QueueNode queueUnit = matrixInfo.getQueueUnit();
      if (queueUnit.restartFlag) {
        queueUnit.setRestartPath(restartPath + "1");
      } else {
        queueUnit.setRestartPath(restartPath + "0");
      }
      runQueue.offer(queueUnit);
      buildQueue(leftCastList, restartPath + matrixInfo.getLeftTreeReStartFlag());
      buildQueue(rightCastList, restartPath + matrixInfo.getRightTreeReStartFlag());
    }


    /**
     * 类功能:执行一次query和check过程
     */
    class MultiRunCaseThread extends Thread {

      public JSONObject pipelineObj;
      public GotReports gotReports;
      public CountDownLatch countDownLatch;
      public TestCaseAI testCaseAI;
      public Map<String, GotTestcaseSnaps> gotTestcaseSnapMap;
      public List<TestCaseAI> getFailCaseAIList;


      public MultiRunCaseThread(String envName, JSONObject pipelineObj, TestCaseAI testCaseAI, GotReports gotReports, CountDownLatch countDownLatch) {
        this.gotReports = gotReports;
        this.pipelineObj = pipelineObj;
        this.countDownLatch = countDownLatch;
        this.testCaseAI = testCaseAI;
        this.gotTestcaseSnapMap = new HashMap<>();
        this.getFailCaseAIList = new ArrayList<>();
      }

      public Map<String, GotTestcaseSnaps> getTestcaseSnapMap() {
        return gotTestcaseSnapMap;
      }

      public List<TestCaseAI> getFailCaseAIList() {
        return getFailCaseAIList;
      }

      @Override
      public void run() {

        GotTestcaseSnaps gotTestcaseSnaps = new GotTestcaseSnaps();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try {
          //每个阶段需要透出的全局数据
          Map<StageName, Object> stageParams = new HashMap<>();
          List<StageName> stageNameList = new ArrayList<>();
          stageNameList.add(StageName.caseRunStage);

          PipelineImpl pipelineExec = new PipelineImpl();
          pipelineExec.build(pipelineObj);

          //更新修改时间
          saveReport(gotReports);
          System.out.print("==========开始执行用例id:" + testCaseAI.getId() + ",时间" + df.format(new Date()) + "===========\n");
          //任务中断
          if (isTaskStop(gotReports)) {
            countDownLatch.countDown();
            return;
          }

          TestRespone testRespone = new TestRespone();
          Date start = new Date();
          //占位符替换
          String testCaseStr = placeholderReplace(testCaseAI);
          //build测试数据
          TestCaseData testCaseDataObj = TestCaseData.build(JSONObject.parseObject(testCaseStr));
          //加入部署数据
          stageParams.put(StageName.deploy, envName);
          //执行每阶段
          for (StageName stageName : stageNameList) {

            BaseStage bs = pipelineExec.flow.get(stageName);
            //表示回归,传入exec中则不会执行重启判断逻辑
            bs.setRunType("2");
            //设置testcase输入
            if (testCaseDataObj.testCaseInputMap.containsKey(stageName)) {
              bs.setIputData(testCaseDataObj.testCaseInputMap.get(stageName));
              bs.setScenarioId(Long.valueOf(testCaseAI.getScenarioId()));
              bs.setTestcaseId(Long.valueOf(testCaseAI.getId()));
              //初始化RunData
              bs.setRunData();
            }
            //设置stage之间传递的参数
            bs.setStageParams(stageParams);
            int retryNum = getRetryNum(bs.getStageJsonObj());
            int i = 0;
            //判断是否需要重试,如果最终结果是
            do {
              i++;
              if (i > 1) {
                int n = i - 1;
                System.out.println("[失败重试运行中],当前重试第" + n + "次");
              }
              bs.exec();
              stageParams = bs.getStageParams();
              //填充返回结果
              testRespone = bs.setTestRespone(testRespone);
            } while (i <= retryNum && !testRespone.getStatus().equals(ResultStatus.SUCCESS));
            gotTestcaseSnaps.setRetryNum(i - 1);
          }
          Date end = new Date();
          //用例快照保存
          gotTestcaseSnaps.setEnvName(envName);
          gotTestcaseSnaps.setRunTime((end.getTime() - start.getTime()) / 1000);
          gotTestcaseSnaps.setRunTimeStr(formatTime((end.getTime() - start.getTime()) / 1000));
          gotTestcaseSnaps.setStatus(testRespone.getStatus().name());
          gotTestcaseSnaps.setAppId(testCaseAI.getAppId());
          gotTestcaseSnaps.setScenarioId(testCaseAI.getScenarioId());
          gotTestcaseSnaps.setDescription(testCaseAI.getDescription());
          gotTestcaseSnaps.setLongDescription(testCaseAI.getLongDescription());
          gotTestcaseSnaps.setName(testCaseAI.getName());
          gotTestcaseSnaps.setTestcaseId(testCaseAI.getId());
          gotTestcaseSnaps.setTestreportId(gotReports.getId());
          gotTestcaseSnaps.setCaseGroup(testCaseAI.getCaseGroup());
          gotTestcaseSnaps.setTag(testCaseAI.getTag());
          gotTestcaseSnaps.setVersion(testCaseAI.getVersion());
          gotTestcaseSnaps.setStatus(testRespone.getStatus().name());
          gotTestcaseSnaps.setContent(pack(testCaseAI, testRespone));
          gotTestcaseSnaps.setIsTrunkFlag(testCaseAI.getIsTrunkFlag());
          testResponeList.add(testRespone);


          if (regAccuracy) {
            //需要获取覆盖
            try {
              GotCaseAccuracy accuracy = new GotCaseAccuracy();
              accuracy.setCaseId(testCaseAI.getId());
              accuracy.setCollectType("single");
              accuracy.setExeId(gotReports.getId());
              accuracy.setCovLine(getCaseAccuracy(testCaseAI));

              caseAccuracyService.save(accuracy);
            } catch (Exception e) {
              System.out.println(e.getMessage());
            }

          }


          try {
            //记录已执行用例数量，计算执行进度
            JSONObject anlysis = null;
            JSONObject basicReport = null;
            try {
              anlysis = JSONObject.parseObject(gotReports.getAnalysis());
              basicReport = anlysis.getJSONObject("basicReport");
            } catch (Exception e) {
              anlysis = new JSONObject();
              basicReport = new JSONObject();
              basicReport.put("sucessCaseNum", 0);
              basicReport.put("failCaseNum", 0);
            }

            Integer successNum = basicReport.getInteger("sucessCaseNum");
            Integer failNum = basicReport.getInteger("failCaseNum");
            if (testRespone.getStatus().name().equals(SUCCESS)) {
              successNum++;
            } else {
              failNum++;
            }

            basicReport.put("caseNum", gotReports.getCaseNum());
            basicReport.put("sucessCaseNum", successNum);
            basicReport.put("failCaseNum", failNum);

            anlysis.put("basicReport", basicReport);
            gotReports.setAnalysis(anlysis.toJSONString());
            saveReport(gotReports);
          } catch (Exception e) {
            //异常则skip..
          }


        } catch (Exception e) {
          gotTestcaseSnaps.setStatus(ResultStatus.ERROR.name());
        }

        //记录成功的用例,失败的用例重试后如果还失败才会记录
        if (gotTestcaseSnaps.getStatus().equals(ResultStatus.SUCCESS.name())) {
          System.out.print("恭喜用例运行通过..." + ",时间" + df.format(new Date()) + "\n");
          saveGotTestcaseSnaps(gotTestcaseSnaps);
        }

        //失败桶记录
        if (gotTestcaseSnaps.getStatus().equals(ResultStatus.ERROR.name())) {
          gotTestcaseSnaps.setStatus("ERROR[已在失败桶等待重试]");
          getFailCaseAIList.add(testCaseAI);
          saveGotTestcaseSnaps(gotTestcaseSnaps);
          System.out.print("===========用例运行失败,进入失败桶等待重试..." + ",时间" + df.format(new Date()) + "===========\n");
        }
        gotTestcaseSnapMap.put(String.valueOf(testCaseAI.getId()), gotTestcaseSnaps);

        countDownLatch.countDown();
      }
    }

    /**
     * 函数功能:并发执行用例query和check
     *
     * @param caseIds
     */
    public void multiQueryAndQuery(List<Long> caseIds, JSONObject dataPrepareRunRes) throws InterruptedException {

      //并行桶
      List<TestCaseAI> runTestcaseListBucket = new ArrayList<>();
      //串行桶
      List<TestCaseAI> runTestcaseListSerialBucket = new ArrayList<>();

      //获取所有id
      for (TestCaseAI testCaseAI : testcaseList) {

        if (caseIds.contains(testCaseAI.getId())) {

          //并行执行
          if (testCaseAI.isParallel()) {
            runTestcaseListBucket.add(testCaseAI);
          }
          //串行执行
          else {
            runTestcaseListSerialBucket.add(testCaseAI);
          }
        }
      }

      if (runTestcaseListBucket.size() == 0 && runTestcaseListSerialBucket.size() == 0) {
        return;
      }


      if (runTestcaseListSerialBucket.size() > 0) {
        System.out.print("开始执行串行用例集:" + runTestcaseListSerialBucket.size() + "\n");

        //串行执行
        for (TestCaseAI testCaseAI : runTestcaseListSerialBucket) {
          envRunBucketMap.put(envName, envRunBucketMap.get(envName) + 1);
          CountDownLatch countDownLatch = new CountDownLatch(1);
          MultiRunCaseThread multiRunCaseThread;
          List<MultiRunCaseThread> threadsList = new ArrayList<>();
          multiRunCaseThread = new MultiRunCaseThread(envName, pipelineObj, testCaseAI, gotReports, countDownLatch);
          multiRunCaseThread.setDaemon(true);
          multiRunCaseThread.start();
          threadsList.add(multiRunCaseThread);
          //主进程阻塞,如果部署线程执行结束则往下
          countDownLatch.await();
          //搜集所有的执行报告
          for (MultiRunCaseThread thread : threadsList) {
            gotTestcaseSnapMap.putAll(thread.getTestcaseSnapMap());
            getFailCaseAIList.addAll(thread.getFailCaseAIList());
          }
        }
      }
      if (runTestcaseListBucket.size() > 0) {

        envRunBucketMap.put(envName, envRunBucketMap.get(envName) + 1);
        //并行执行
        CountDownLatch countDownLatch = new CountDownLatch(runTestcaseListBucket.size());
        MultiRunCaseThread multiRunCaseThread;
        System.out.print("开始并行执行用例数:" + runTestcaseListBucket.size());
        List<MultiRunCaseThread> threadsList = new ArrayList<>();

        for (TestCaseAI testCaseAI : runTestcaseListBucket) {
          multiRunCaseThread = new MultiRunCaseThread(envName, pipelineObj, testCaseAI, gotReports, countDownLatch);
          multiRunCaseThread.setDaemon(true);
          multiRunCaseThread.start();
          threadsList.add(multiRunCaseThread);
          Thread.currentThread().sleep(1000);
        }
        //主进程阻塞,如果部署线程执行结束则往下
        countDownLatch.await();
        //搜集所有的执行报告
        for (MultiRunCaseThread thread : threadsList) {
          gotTestcaseSnapMap.putAll(thread.getTestcaseSnapMap());
          getFailCaseAIList.addAll(thread.getFailCaseAIList());
        }
      }

    }


    /**
     * 函数功能:统计数据准备的冗余度和总量
     *
     * @param unit
     */
    public void calStaticRunData(QueueNode unit) {

      Map<String, RunDs> runDataStaticMap = envRunDataStaticMap.get(envName);

      for (HashMap<String, LinkedList<DetailDataInfo>> typeInfo : unit.getPrepareData()) {

        for (String type : typeInfo.keySet()) {
          LinkedList<DetailDataInfo> list = typeInfo.get(type);
          for (DetailDataInfo detailDataInfo : list) {
            String ds = detailDataInfo.getDsName();
            String typeDs = type + ":" + getTypeDs(ds);

            if (!runDataStaticMap.containsKey(typeDs)) {
              runDataStaticMap.put(typeDs, new RunDs());
            }
            runDataStaticMap.get(typeDs).setAllNum(runDataStaticMap.get(typeDs).getAllNum() + unit.getCaseNum());
            runDataStaticMap.get(typeDs).setRepeatNum(runDataStaticMap.get(typeDs).getRepeatNum() + unit.getCaseNum() - 1);
          }
        }
      }
    }

    /**
     * 函数功能:数据准备合并过程
     *
     * @param dumpData
     * @param prepareData
     * @return
     */
    public LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> mergeRunData(LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpData, LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> prepareData) {


      //dump集合为空
      if (dumpData.size() == 0) {
        dumpData = prepareData;
      } else {
        for (int groupNum = 0; groupNum < prepareData.size(); groupNum++) {
          HashMap<String, LinkedList<DetailDataInfo>> dumpDataMap = dumpData.get(groupNum);
          HashMap<String, LinkedList<DetailDataInfo>> dataMap = prepareData.get(groupNum);

          for (String dataType : dataMap.keySet()) {
            LinkedList<DetailDataInfo> dataList = dataMap.get(dataType);
            LinkedList<DetailDataInfo> dumpDataList = dumpDataMap.get(dataType);
            //如果全新的datatype
            if (!dumpDataMap.containsKey(dataType)) {
              dumpDataMap.put(dataType, dataMap.get(dataType));
            }
            //如果已存在
            else {
              for (int listNum = 0; listNum < dataList.size(); listNum++) {
                Boolean flag = true;

                DetailDataInfo detailDataInfo = dataList.get(listNum);
                //遍历dump
                for (int listDumpNum = 0; listDumpNum < dumpDataList.size(); listDumpNum++) {
                  DetailDataInfo dumpDetailDataInfo = dumpDataList.get(listDumpNum);
                  //如果ds相等,则record直接合并
                  if (dumpDetailDataInfo.getDsName().equals(detailDataInfo.getDsName())) {
                    flag = false;
                    dumpDetailDataInfo.getData().addAll(detailDataInfo.getData());
                  }
                }
                //如果不存在ds
                if (flag) {
                  dumpDataList.add(detailDataInfo);
                }
              }
            }
          }
        }
      }

      return dumpData;
    }


    /**
     * 函数功能:
     * 按照深度优先方式执行数据准备 && 并行发送用例和校验结果
     */
    public void multiLayerPrepareAndRun() {

      LinkedList<HashMap<String, LinkedList<DetailDataInfo>>> dumpLayerData = new LinkedList<>();

      Long dumpTreeTime = Long.valueOf(0);
      Long runTreeTime = Long.valueOf(0);

      //队列执行,先进先出
      for (QueueNode unit : runQueue) {
        //如果节点是数据准备类型,则进行合并,后续在一次执行
        if (unit.getDataType().equals(DATA_PREPARE)) {
          //合并
          dumpLayerData = mergeRunData(dumpLayerData, unit.getPrepareData());
          //统计数据冗余度
          calStaticRunData(unit);
        }
        //如果节点是发送校验类型
        else {
          //运行分层数据准备
          Date start = new Date();
          JSONObject dataPrepareRunRes = dataPrepareRun(dumpLayerData, envName, pipelineObj, DUMP_TREE_BUCKET);
          Date end = new Date();
          dumpTreeTime += end.getTime() - start.getTime();

          //并发执行case
          try {
            start = new Date();
            if (isTaskStop(gotReports)) {
              return;
            }
            multiQueryAndQuery(unit.getCaseids(), dataPrepareRunRes);
            end = new Date();
            runTreeTime += end.getTime() - start.getTime();

          } catch (Exception e) {
            System.out.print("分层快速执行失败:原因:" + Toolkit.getErrorStackTrace(e) + "\n");
          }

          //清空分层数据集
          dumpLayerData = new LinkedList<>();
        }
      }
      envBucketCostMap.get(envName).put(DUMP_TREE_BUCKET, dumpTreeTime);
      envBucketCostMap.get(envName).put(QUERY_CHECK_BUCKET, runTreeTime);
    }


    /**
     * 函数功能:clone队列
     *
     * @param queue
     * @return
     */
    public Queue<QueueNode> cloneQueue(Queue<QueueNode> queue) {
      Queue<QueueNode> cloneQueue = new LinkedList<>();
      for (QueueNode queueNode : queue) {
        cloneQueue.offer((QueueNode) queueNode.clone());
      }
      return cloneQueue;
    }


    /**
     * 函数功能:队列重排
     * 将执将需要重启的链路放置尾部
     *
     * @return
     */
    public Queue<QueueNode> reSortQueue() {
      Queue<QueueNode> eachQueue = new LinkedList<>();
      Queue<QueueNode> queueList1 = new LinkedList<>();
      Queue<QueueNode> queueList2 = new LinkedList<>();

      //扫描队列
      for (QueueNode queueNode : runQueue) {

        eachQueue.offer(queueNode);

        if (queueNode.getDataType().equals(QUERY_CHECK) && queueNode.getRestartPath().contains("1")) {
          queueList1.addAll(cloneQueue(eachQueue));
          eachQueue = new LinkedList<>();
        }

        if (queueNode.getDataType().equals(QUERY_CHECK) && !queueNode.getRestartPath().contains("1")) {
          queueList2.addAll(cloneQueue(eachQueue));
          eachQueue = new LinkedList<>();
        }

      }

      //逆序重排队列
      queueList2.addAll(queueList1);
      return queueList2;
    }


  }

  public String getCaseAccuracy(TestCaseAI testCaseAI) {
    //需要用户基于实际使用的覆盖统计方案，获取每个用例执行后的覆盖代码行
    //demo中通过mock数据，获取用例覆盖代码
    String res = "";
    try {
      JSONObject caseQuery = JSONObject.parseObject(testCaseAI.getCaseRunStage().get(0).getData().get(0).input);
      List<String> covList = CovMock.getCaseCovMock(caseQuery);

      for (String line : covList) {
        res += (line + "\n");
      }
      res = res.trim();
    } catch (Exception e) {
      //
    }
    return res;

  }

  /**
   * 类功能:单环境失败用例集执行线程
   */
  class SingleEnvRunCaseThread extends Thread {
    public List<TestCaseAI> serialCaseList;
    public Map<String, String> deployMap;
    public JSONObject pipelineObj;
    public GotReports gotReports;
    public CountDownLatch countDownLatch;
    public String envName;


    public SingleEnvRunCaseThread(List<TestCaseAI> serialCaseList, Map<String, String> deployMap, JSONObject pipelineObj, GotReports gotReports, CountDownLatch countDownLatch, String envName) {
      this.serialCaseList = serialCaseList;
      this.deployMap = deployMap;
      this.pipelineObj = pipelineObj;
      this.gotReports = gotReports;
      this.countDownLatch = countDownLatch;
      this.envName = envName;

    }

    /**
     * 函数功能:失败用例重跑策略
     * 说明:用户可自定义,比如全部失败的用例只跑其中10%,小于某个阈值的用例集需要全部重跑
     *
     * @param testCaseAIList
     * @return
     */
    public List<TestCaseAI> selectReRunCaseList(List<TestCaseAI> testCaseAIList) {

      int selectNum = 0;
      int limit = testCaseAIList.size();


      //策略:此处limit可限制重跑个数阈值
      if (testCaseAIList.size() > limit) {
        selectNum = limit;
      } else {
        selectNum = testCaseAIList.size();
      }

      List<TestCaseAI> selectCaseAIList = new ArrayList<>();

      for (int i = 0; i < selectNum; i++) {
        selectCaseAIList.add(testCaseAIList.get(i));
      }
      for (int i = selectNum; i < testCaseAIList.size(); i++) {
        TestCaseAI testCaseAI = testCaseAIList.get(i);
        GotTestcaseSnaps gotTestcaseSnaps = gotTestcaseSnapMap.get(String.valueOf(testCaseAI.getId()));
        gotTestcaseSnaps.setStatus(ResultStatus.ERROR.name() + "[未重试]");
        gotTestcaseSnaps.setIsTrunkFlag(testCaseAI.getIsTrunkFlag());
        gotTestcaseSnapMap.put(String.valueOf(testCaseAI.getId()), gotTestcaseSnaps);
        gotTestcaseSnapsDao.update(gotTestcaseSnaps);
      }
      return selectCaseAIList;
    }


    /**
     * 函数功能:重跑失败用例并进行最终状态设置
     *
     * @param testCaseAIList
     * @param deployMap
     * @param pipelineObj
     * @param gotReports
     * @return
     * @throws IOException
     */
    public List<TestCaseAI> runFailCaseList(List<TestCaseAI> testCaseAIList, Map<String, String> deployMap, JSONObject pipelineObj, GotReports gotReports) throws IOException {

      //每个阶段需要透出的全局数据
      Map<StageName, Object> stageParams = new HashMap<>();
      List<StageName> stageNameList = new ArrayList<>();
      stageNameList.add(StageName.prepareData);
      stageNameList.add(StageName.caseRunStage);
      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式

      if (testCaseAIList.size() == 0) {
        return testCaseAIList;
      }
      PipelineImpl pipelineExec = new PipelineImpl();
      pipelineExec.build(pipelineObj);

      List<GotTestcaseSnaps> failTestcaseList = new ArrayList<>();
      int failCaseNum = 0;
      //重跑策略选取用例集
      testCaseAIList = selectReRunCaseList(testCaseAIList);

      //重跑失败桶用例集
      for (TestCaseAI testCaseAI : testCaseAIList) {

        try {


          System.out.print("==========执行失败桶用例id:" + testCaseAI.getId() + ",时间" + df.format(new Date()) + "===========\n");
          appId = testCaseAI.getAppId();
          scenarioId = testCaseAI.getScenarioId();

          System.out.print("失败重试和智能排查阶段:执行用例,id:" + testCaseAI.getId());
          //任务中断
          if (isTaskStop(gotReports)) {
            return testCaseAIList;
          }
          TestRespone testRespone = new TestRespone();

          //重试阶段需准备全部数据
//                    testCaseAI.setPrepareDataSnap(testCaseAI.getPrepareData());

          //占位符替换
          String testCaseStr = placeholderReplace(testCaseAI);
          //build测试数据
          TestCaseData testCaseDataObj = TestCaseData.build(JSONObject.parseObject(testCaseStr));
          //加入部署数据
          stageParams.put(StageName.deploy, deployMap);

          //执行每阶段
          for (StageName stageName : stageNameList) {

            BaseStage bs = pipelineExec.flow.get(stageName);
            //设置testcase输入
            if (testCaseDataObj.testCaseInputMap.containsKey(stageName)) {
              bs.setIputData(testCaseDataObj.testCaseInputMap.get(stageName));
              bs.setScenarioId(Long.valueOf(testCaseAI.getScenarioId()));
              bs.setTestcaseId(Long.valueOf(testCaseAI.getId()));
              //初始化RunData
              bs.setRunData();
            }
            //设置stage之间传递的参数
            bs.setStageParams(stageParams);
            int retryNum = getRetryNum(bs.getStageJsonObj());
            int i = 0;
            //判断是否需要重试,如果最终结果是
            do {
              i++;
              if (i > 1) {
                int n = i - 1;
                logger.info("[失败重试运行中],当前重试第" + n + "次");

              }
              bs.exec();
              stageParams = bs.getStageParams();
              //填充返回结果
              testRespone = bs.setTestRespone(testRespone);
            } while (i <= retryNum && !testRespone.getStatus().equals(ResultStatus.SUCCESS));
          }

          GotTestcaseSnaps gotTestcaseSnaps = gotTestcaseSnapMap.get(String.valueOf(testCaseAI.getId()));
          saveReport(gotReports);
          //快照页中保存更多结果信息
          gotTestcaseSnaps.setContent(pack(testCaseAI, testRespone));

          //如果成功了,则是要修改最终的状态
          if (testRespone.getStatus().name().equals(ResultStatus.SUCCESS.name())) {

            gotTestcaseSnaps.setStatus(ResultStatus.SUCCESS.name());
            gotTestcaseSnapMap.put(String.valueOf(testCaseAI.getId()), gotTestcaseSnaps);
            //快照记录
            gotTestcaseSnapsDao.update(gotTestcaseSnaps);

          }
          //如果失败了,更新智能排查和动态归因
          else {
            failCaseNum++;
            gotTestcaseSnaps.setStatus(ResultStatus.ERROR.name());
            gotTestcaseSnaps.setIsTrunkFlag(testCaseAI.getIsTrunkFlag());
            failTestcaseList.add(gotTestcaseSnaps);
            gotTestcaseSnapMap.put(String.valueOf(testCaseAI.getId()), gotTestcaseSnaps);

          }
        } catch (Exception e) {
          System.out.print("[重试阶段]用例回归过程异常:" + Toolkit.getErrorStackTrace(e));
        }

      }

      return testCaseAIList;

    }

    @Override
    public void run() {
      try {
        runFailCaseList(serialCaseList, deployMap, pipelineObj, gotReports);
      } catch (Exception e) {
        System.out.print("重试桶线程异常:" + Toolkit.getErrorStackTrace(e));
      }
      countDownLatch.countDown();
    }
  }


  /**
   * 函数功能:失败用例重跑
   *
   * @param gotReports
   * @return
   */
  public String reRunFailCaseBuckets(GotReports gotReports) {

    try {
      if (failCaseBuckets.size() > 0) {

        gotReports.setStatus("失败桶重跑中");
        saveReport(gotReports);
        CountDownLatch countDownLatch = new CountDownLatch(failCaseBuckets.size());
        SingleEnvRunCaseThread singleEnvRunCaseThread;
        List<SingleEnvRunCaseThread> threadsList = new ArrayList<>();

        Map<String, String> deployMap = null;
        //重跑
        for (String envName : failCaseBuckets.keySet()) {
          FailCaseBucket failCaseBucket = failCaseBuckets.get(envName);
          JSONObject pipelineObj = failCaseBucket.getPipelineObj();
          List<TestCaseAI> testCaseAIList = failCaseBucket.getTestCaseAIList();
          System.out.print(("执行失败用例集,用例数:" + testCaseAIList.size() + ",环境名为:" + envName));
          singleEnvRunCaseThread = new SingleEnvRunCaseThread(testCaseAIList, deployMap, pipelineObj, gotReports, countDownLatch, envName);
          singleEnvRunCaseThread.setDaemon(true);
          singleEnvRunCaseThread.start();
          threadsList.add(singleEnvRunCaseThread);
          Thread.currentThread().sleep(2000);
        }
        countDownLatch.await();
      }
    } catch (Exception e) {
      System.out.print("失败桶运行过程出现异常,请检查!" + Toolkit.getErrorStackTrace(e));

    }


    return "";
  }


  /**
   * 函数功能:保存测试报告
   *
   * @param gotReports
   */
  public void saveReport(GotReports gotReports) {
    gotReportsDao.update(gotReports);
  }

  /**
   * 函数功能:插入测试报告
   *
   * @param gotReports
   */
  public void insertReport(GotReports gotReports) {
    gotReportsDao.insert(gotReports);
  }

  /**
   * 函数功能:保存测试用例快照
   *
   * @param gotTestcaseSnaps
   * @return
   */
  public Long saveGotTestcaseSnaps(GotTestcaseSnaps gotTestcaseSnaps) {
    return Long.valueOf(gotTestcaseSnapsDao.insert(gotTestcaseSnaps));
  }


  /**
   * 函数功能:占位符替换
   *
   * @param testCase
   * @return
   */
  public String placeholderReplace(Object testCase) {

    //将转化为json串,然后整体来进行替换
    Gson gosn = new Gson();
    String testCaseJsonStr = gosn.toJson(testCase);
    return Toolkit.placeholderReplace(testCaseJsonStr);
  }


  /**
   * 功能:预处理用例,将每个用例,给出用例的是否能并行,以及用例数据准备等信息.
   *
   * @param testcaseList
   * @return
   */
  public List<TestCaseAI> forkTestcaseAIList(List<TestCaseInput> testcaseList) {

    List<TestCaseAI> testCaseAIList = new ArrayList<>();

    for (TestCaseInput testCase : testcaseList) {

      testCaseAIList.add(TestCaseAI.packTestCaseAI(testCase));
    }

    return testCaseAIList;
  }


  /**
   * 类功能:多环境caseBycase执行线程
   */
  class MultiEnvSimpleCaseThread extends Thread {

    public List<TestCaseAI> serialCaseList;
    public JSONObject pipelineObj;
    public GotReports gotReports;
    public List<TestRespone> testResponeList;
    public CountDownLatch countDownLatch;
    public String envName;


    public MultiEnvSimpleCaseThread(List<TestCaseAI> serialCaseList, JSONObject pipelineObj, GotReports gotReports, CountDownLatch countDownLatch, String envName) {
      this.serialCaseList = serialCaseList;
      this.pipelineObj = pipelineObj;
      this.gotReports = gotReports;
      this.countDownLatch = countDownLatch;
      this.envName = envName;
    }

    public List<TestRespone> getResult() {
      return testResponeList;
    }


    /**
     * 函数功能:
     *
     * @return
     * @throws Exception
     */
    public List<TestRespone> runCaseList(List<TestCaseAI> serialCaseList, JSONObject pipelineObj, GotReports gotReports, String envName) {

      try {
        Map<StageName, Object> stageParams = new HashMap<>();
        List<StageName> stageNameList = new ArrayList<>();
        stageNameList.add(StageName.prepareData);
        stageNameList.add(StageName.caseRunStage);
        List<TestRespone> testResponeList = new ArrayList<>();

        if (serialCaseList.size() == 0) {
          return testResponeList;
        }

        PipelineImpl pipelineExec = new PipelineImpl();
        pipelineExec.build(pipelineObj);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Integer failNum = 0;
        Integer successNum = 0;
        //遍历执行用例集
        for (TestCaseAI testCaseAI : serialCaseList) {

          envRunBucketMap.put(envName, envRunBucketMap.get(envName) + 1);
          System.out.print("==========开始执行用例id:" + testCaseAI.getId() + ",时间" + df.format(new Date()) + "===========\n");
          //任务中断
          if (isTaskStop(gotReports)) {
            return null;
          }

          TestRespone testRespone = new TestRespone();
          GotTestcaseSnaps gotTestcaseSnaps = new GotTestcaseSnaps();
          try {
            Date start = new Date();
            //占位符替换
            String testCaseStr = placeholderReplace(testCaseAI);
            //build测试数据
            TestCaseData testCaseDataObj = TestCaseData.build(JSONObject.parseObject(testCaseStr));
            Map<String, RunDs> runDsMap = envRunDataStaticMap.get(envName);

            //执行每阶段
            for (StageName stageName : stageNameList) {

              BaseStage bs = pipelineExec.flow.get(stageName);
              //设置testcase输入
              if (testCaseDataObj.testCaseInputMap.containsKey(stageName)) {
                bs.setIputData(testCaseDataObj.testCaseInputMap.get(stageName));
                bs.setScenarioId(Long.valueOf(testCaseAI.getScenarioId()));
                bs.setTestcaseId(Long.valueOf(testCaseAI.getId()));
                //初始化RunData
                bs.setRunData();
              }
              //设置stage之间传递的参数
              bs.setStageParams(stageParams);
              int retryNum = getRetryNum(bs.getStageJsonObj());
              int i = 0;
              //判断是否需要重试,如果最终结果是
              do {
                i++;
                if (i > 1) {
                  int n = i - 1;
                  System.out.println("[失败重试运行中],当前重试第" + n + "次");
                }
                bs.beforeExec();
                bs.exec();
                bs.afterExec();
                stageParams = bs.getStageParams();
                //填充返回结果
                testRespone = bs.setTestRespone(testRespone);
              } while (i <= retryNum && !testRespone.getStatus().equals(ResultStatus.SUCCESS));
              gotTestcaseSnaps.setRetryNum(i - 1);

              //统计每次数据准备花费的消耗
              try {
                if (stageName.equals(StageName.prepareData)) {
                  List<RunData> runDataList = bs.getRunDataList();
                  if (runDataList.size() > 0) {
                    for (RunData runData : runDataList) {

                      if (runData.getRunDsMap().size() == 0) {
                        continue;
                      }

                      for (String dsStr : runData.getRunDsMap().keySet()) {
                        if (!runDsMap.containsKey(dsStr)) {
                          runDsMap.put(dsStr, new RunDs());
                        }
                        runDsMap.get(dsStr).setRecordNum(runDsMap.get(dsStr).getRecordNum() + runData.getRunDsMap().get(dsStr).getRecordNum());
                        runDsMap.get(dsStr).setCost(runDsMap.get(dsStr).getCost() + runData.getRunDsMap().get(dsStr).getCost());
                      }

                    }
                  }
                }
              } catch (Exception e) {
                //
              }
            }
            Date end = new Date();
            if (testCaseAI.getConflictDs() != null) {
              List<String> dsList = new ArrayList<>();
              for (String ds : testCaseAI.getConflictDs().keySet()) {
                dsList.add(ds + ":[" + Toolkit.implode(",", testCaseAI.getConflictDs().get(ds)) + "]");
              }
              gotTestcaseSnaps.setConflictDesc(Toolkit.implode(",", dsList));
            }
            //用例快照保存
            gotTestcaseSnaps.setParallel(false);
            gotTestcaseSnaps.setEnvName(envName);
            gotTestcaseSnaps.setRunTime((end.getTime() - start.getTime()) / 1000);
            gotTestcaseSnaps.setRunTimeStr(formatTime((end.getTime() - start.getTime()) / 1000));
            gotTestcaseSnaps.setStatus(testRespone.getStatus().name());
            gotTestcaseSnaps.setAppId(testCaseAI.getAppId());
            gotTestcaseSnaps.setScenarioId(testCaseAI.getScenarioId());
            gotTestcaseSnaps.setDescription(testCaseAI.getDescription());
            gotTestcaseSnaps.setLongDescription(testCaseAI.getLongDescription());
            gotTestcaseSnaps.setName(testCaseAI.getName());
            gotTestcaseSnaps.setTestcaseId(testCaseAI.getId());
            gotTestcaseSnaps.setTestreportId(gotReports.getId());
            gotTestcaseSnaps.setCaseGroup(testCaseAI.getCaseGroup());
            gotTestcaseSnaps.setTag(testCaseAI.getTag());
            gotTestcaseSnaps.setVersion(testCaseAI.getVersion());
            gotTestcaseSnaps.setStatus(testRespone.getStatus().name());
            gotTestcaseSnaps.setContent(pack(testCaseAI, testRespone));
            gotTestcaseSnaps.setIsTrunkFlag(testCaseAI.getIsTrunkFlag());
            testResponeList.add(testRespone);

            //记录已执行用例数量，计算执行进度
            if (testRespone.getStatus().name().equals(SUCCESS)) {
              successNum++;
            } else {
              failNum++;
            }


            if (regAccuracy) {
              //需要获取覆盖
              try {
                GotCaseAccuracy accuracy = new GotCaseAccuracy();
                accuracy.setCaseId(testCaseAI.getId());
                accuracy.setCollectType("single");
                accuracy.setExeId(gotReports.getId());
                accuracy.setCovLine(getCaseAccuracy(testCaseAI));

                caseAccuracyService.save(accuracy);
              } catch (Exception e) {
                System.out.println(e.getMessage());
              }

            }

            JSONObject anlysis = new JSONObject();
            JSONObject basicReport = new JSONObject();
            basicReport.put("caseNum", gotReports.getCaseNum());
            basicReport.put("sucessCaseNum", successNum);
            basicReport.put("failCaseNum", failNum);
            anlysis.put("basicReport", basicReport);
            gotReports.setAnalysis(anlysis.toJSONString());
            saveReport(gotReports);
          } catch (Exception e) {
            logger.error("run case error: " + Toolkit.getErrorStackTrace(e));
            gotTestcaseSnaps.setStatus(ResultStatus.ERROR.name());

          }

          //记录成功的用例,失败的用例重试后如果还失败才会记录
          if (gotTestcaseSnaps.getStatus().equals(ResultStatus.SUCCESS.name())) {
            System.out.println("恭喜用例运行通过..." + ",时间" + df.format(new Date()) + "\n");
            saveGotTestcaseSnaps(gotTestcaseSnaps);
          }

          //失败桶记录
          if (gotTestcaseSnaps.getStatus().equals(ResultStatus.ERROR.name())) {
            gotTestcaseSnaps.setStatus("ERROR[已在失败桶等待重试]");
            saveGotTestcaseSnaps(gotTestcaseSnaps);
            logger.info("===========用例运行失败,进入失败桶等待重试..." + ",时间" + df.format(new Date()) + "===========");

            if (!failCaseBuckets.containsKey(envName)) {
              testCaseAI.setCaseSnapId(gotTestcaseSnaps.getId());
              failCaseBuckets.put(envName, new FailCaseBucket());
              failCaseBuckets.get(envName).setPipelineObj(pipelineObj);
              failCaseBuckets.get(envName).setGotReports(gotReports);
              failCaseBuckets.get(envName).setTestCaseAIList(new ArrayList<>());
              failCaseBuckets.get(envName).getTestCaseAIList().add(testCaseAI);
            } else {
              failCaseBuckets.get(envName).getTestCaseAIList().add(testCaseAI);
            }
          }
          gotTestcaseSnapMap.put(String.valueOf(testCaseAI.getId()), gotTestcaseSnaps);

        }

        return testResponeList;
      } catch (Exception e) {
        logger.error("运行过程出现异常,请检查!" + Toolkit.getErrorStackTrace(e));
        return null;
      }
    }

    @Override
    public void run() {
      try {

        testResponeList = runCaseList(serialCaseList, pipelineObj, gotReports, envName);

      } catch (Exception e) {
        System.out.println("[普通回归]用例回归过程异常:" + Toolkit.getErrorStackTrace(e));
      }
      countDownLatch.countDown();
    }
  }


  public int getRetryNum(JSONObject pipelineBsJson) {
    int retryNum = 0;
    if (pipelineBsJson != null && pipelineBsJson.containsKey(PARAMS)) {
      JSONObject paramsObj = (JSONObject) pipelineBsJson.get(PARAMS);
      retryNum = paramsObj.containsKey("retryNum") ? (int) paramsObj.get("retryNum") : 0;
    }
    return retryNum;
  }


  /**
   * 函数功能:将用例快照页信息进行聚合
   *
   * @param testcase
   * @param testRespone
   * @return
   */
  public String pack(TestCaseAI testcase, TestRespone testRespone) {
    JSONObject content = new JSONObject();

    testRespone.setTestCaseId(testcase.getId());
    content.put(StageName.prepareData.name(), testcase.getPrepareDataSnap());
    content.put(StageName.caseRunStage.name(), testcase.getCaseRunStage());

    //去掉reponse中的log
    try {
      for (String groupName : testRespone.getRunStage().keySet()) {
        for (RunData runData : testRespone.getRunStage().get(groupName)) {
          runData.setLog("no log");
        }
      }
    } catch (Exception e) {

    }

    content.put(StageName.response.name(), testRespone.getRunStage());
    return gson.toJson(content);
  }


  /**
   * 函数功能:对用例列表进行分组
   *
   * @param parallelCaseList
   * @param parallelNum
   * @param i
   * @return
   */
  public List<TestCaseAI> testCaseSplit(List<TestCaseAI> parallelCaseList, int parallelNum, int i) {

    List<TestCaseAI> calCaseList = new ArrayList<>();
    int divNum = parallelCaseList.size() / parallelNum;

    //返回为空
    if (parallelCaseList == null || parallelCaseList.size() == 0) {
      return calCaseList;
    }
    //如果case数不大于并行数,每次只返回一个
    else if (divNum == 0) {
      if (parallelCaseList.size() > i) {
        calCaseList.add(parallelCaseList.get(i));
      }
    }
    //如果case数大于并行数,每次只返回一个
    else {
      int modNum = parallelCaseList.size() % parallelNum;
      int start = divNum * i;
      int end = parallelCaseList.size();
      //最后一组
      if (i + 1 < parallelNum) {
        end = start + divNum;
      }
      for (int n = start; n < end; n++) {
        calCaseList.add(parallelCaseList.get(n));
      }
    }
    return calCaseList;
  }

  /**
   * 函数功能:对用例集根据环境数进行串行和并行分组
   *
   * @param testcaseGroup
   * @param parallelNum
   * @param i
   * @return
   */
  public Map<String, List<TestCaseAI>> getTestcaseEnvGroup(Map<String, List<TestCaseAI>> testcaseGroup, int parallelNum, int i) {

    Map<String, List<TestCaseAI>> testcaseEnvGroup = new HashMap<>();
    //caseBycase的模式下,并行桶切分就暂时注释
    testcaseEnvGroup.put(SERIAL, testCaseSplit(testcaseGroup.get(SERIAL), parallelNum, i));
    return testcaseEnvGroup;
  }


  /**
   * 函数功能:将运行时间格式化
   *
   * @param seconds 01:15:33
   * @return
   */
  public String formatTime(long seconds) {

    long temp;
    StringBuffer sb = new StringBuffer();
    temp = seconds / 3600;
    sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");

    temp = seconds % 3600 / 60;
    sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");

    temp = seconds % 3600 % 60;
    sb.append((temp < 10) ? "0" + temp : "" + temp);

    return sb.toString();
  }


  public static void main(String[] args)
      throws Exception {


  }
}
